CakePHP Fixture Factoriesの登場によって変化する、PHPプロジェクトにおけるテストフィクスチャ管理の選択肢
CakePHP Fixture Factories の登場によって変化する、PHPプロジェクトにおけるテストフィクスチャ管理の選択肢 by takoba | トーク | PHPerKaigi 2022 #phperkaigi - fortee.jp
TODO: これを読むこと
SWE本(書籍「Software Engineering at Google」)
現在時刻が関わるユニットテストから、テスト容易性設計を学ぶ - t-wadaのブログ
フィクスチャのアップグレード - CakePHP 4.x Strawberry Cookbook
「こんなところも?」 CakePHP4・phpunitのアップグレードに伴う変更箇所 - コネヒト開発者ブログ
CakePHP4.3 でテストがかなり変わってた - Fusic Tech Blog
構造
概要
前提
(おさらい)xUnitとは?
cf. xUnit - Wikipedia
コンピュータプログラムの単体テスト(ユニットテスト)を行うためのテスティングフレームワークの総称である。これらのフレームワークでは、関数やクラスなど、ソフトウェアの様々な要素(ユニット)をテストすることができる。xUnitフレームワークの主な利点は、テストを自動化できること、同じテストを何度も書かずに済むこと、個々のテストの結果がどうあるべきかを覚えておかなくても良いことである。
この中の "xUnitの設計"項の中に"テストスイート"、"テストの実行"、"アサーション"と並んで"テストフィクスチャ"がある
では What's テストフィクスチャ
cf. xUnit - Wikipedia
テストを実行、成功させるために必要な状態や前提条件の集合を、フィクスチャ(英語版)と呼ぶ。これらはテストコンテキストとも呼ばれる。開発者はテストの実行前にテストに適した状態を整え、テスト実行後に元の状態を復元することが望ましい。
テストフィクスチャは前提条件となるもの全てを指してる
データベースにレコードを登録するアプリだったら、データベース(および事前に必要になるレコード)がテストフィクスチャ
文字をトークナイズするライブラリだったら、自然言語のテキストたちがテストフィクスチャ
小さなライブラリなら単なる文字列とか配列とかもそう
つまり、自動テストにおけるテストフィクスチャは、数多の種類のデータを扱うであろう、ほぼ全てのソフトウェアにおいて必要になるテクニック
その中でもデータベースと密接に連携する Web アプリケーションにおいては、データベースにデータを投入し、そのデータをテストフィクスチャとして利用するテクニックは初期の頃から利用されてきました。
#要出典 なのだが、Rails 2.x の段階ではすでにテストフィクスチャについて RailsGuides に言及されていたので、まあフルスタックフレームワークが登場した初期の頃から存在すると言ってよさそう
例えば、MVCフレームワークを採用したアプリでいうModel層のテストケースがほしいとする
入力されたデータが正常にデータベースに登録されるか、を検査したい
それユニットテストか...?って話はご愛嬌
「どんくらい昔からやってるのそれ?」
Rails 2.x の段階でModelクラスのテストケースについて言及されてる のよね
そして、Rails 2.x の段階ではすでにテストフィクスチャについて RailsGuides に言及されていた ので、まあフルスタックフレームワークが登場した初期の頃(2008年くらい)から存在すると言ってよさそう
もっと言うと rails/rails v1.0.0 の activerecord にも普通にfixtures.rb があった ので、rails正式リリース当初(2005年くらい)から存在してそう
ちょっとこの辺調べきれてないので誰かたのんだ...
cf. Rubyのテスティングフレームワークの歴史(2014年版) - 2014-11-06 - ククログ
cf. test-unit - Ruby用単体テストフレームワーク
@t_wada「エリック・ガンマとケント・ベックが、チューリッヒからアトランタまでのフライトの中で"SUnitをJUnitに移植する"という伝説のペアプロを行った、ってのがあって、それが今年(2022年)で25年」
cf. https://anchor.fm/textafm/episodes/9--The-20th-Anniversary-of-TDD-e1fbh4j
@t_wada「テスト駆動開発(TDD; Test-Driven Development)が生まれたのも2002年」とも言ってるので、割とフルスタックフレームワークでテストが整備されたのってRuby on Railsは割と早かったんだと思う
@t_wada「「TDD原著が出たのも言葉が生まれた直後。2002年だった、と」
もしかしたらStrutsとかで用意されてる可能性はある
e.g. テストフィクスチャがフルスタックフレームワークでどう装備されてる??
ためしにRuby on Railsの場合〜〜
YAMLファイルとか定義してDBに取り込む ActiveRecord::FixtureSet ってクラスがある
cf. 4.2. テストフィクスチャのしくみ | Rails テスティングガイド - Railsガイド
CakePHPの場合〜〜〜
PHPファイルに配列を書いて定義するかんじ
cf. フィクスチャー | テスト - CakePHP 4.x Strawberry Cookbook
Laravelの場合〜〜〜
おっと?? Model Factories という概念を使ってるね??
cf. Defining Model Factories | Database Testing - Laravel - The PHP Framework For Web Artisans
Laravel 5.3くらいからあるっぽい?誰か補足して...
cf. Writing Factories | Database Testing - Laravel - The PHP Framework For Web Artisans
テストフィクスチャの種類を大別すると
あっ Test fixture - Wikipedia に載ってたわ
cf. Setup | Test fixture - Wikipedia
In-line setup(インライン)
In-line setup creates the test fixture in the same method as the rest of the test. While in-line setup is the simplest test fixture to create, it leads to duplication when multiple tests require the same initial data.
テストケース内で定義される形式
Delegate setup(委任)
Delegate setup places the test fixture in a separate standalone helper method that is accessed by multiple test methods.
複数のテストメソッドから呼ばれるstandaloneなヘルパーメソッドを経由して呼び出される形式
Implicit setup(暗黙的)
Implicit setup places the test fixture in a setup method which is used to set up multiple test methods. This differs from delegate setup in that the overall setup of multiple tests is in a single setup method where the test fixture gets created rather than each test method having its own setup procedures and linking to an external test fixture.
単一のセットアップ処理で生成されて、それが複数のテストメソッドから呼び出される
で、書籍「xUnit Test Patterns」では、これらはすべてFresh Fixtureという戦略の上にあるとされているよ
対になるShared Fixtureという戦略もある
cf. Shared Fixture at XUnitPatterns.com
To execute an automated test, we require a text fixture that is well understood and completely deterministic. Setting up a Fresh Fixture (page X) can be time consuming, especially when dealing with complex system state stored in a test database.
完全に決定論的に理解された"テストフィクスチャ"が自動テストの実行には必要である、 #トノコト
"text fixture" はtypoっぽい
cf. Unit testing: TEST Fixture, or TEXT Fixture - Stack Overflow
cf. 決定論 - Wikipedia
決定論(けっていろん、英: determinism、羅: determinare)とは、あらゆる出来事は、その出来事に先行する出来事のみによって決定している、とする哲学的な立場。
要は固定されたテキストフィクスチャが用いられるべき、 #トノコト
つまり、自動テストの高速化にはできるだけテストフィクスチャを一括で用意して、それを用いる方が効果的なんだよ!って話なんだね
そもそも自動テストに高速化って必要なんだっけ?? #要出典
まあ大風呂敷広げたけど、結局は何を使うのが正解?
実行速度を求めるならば Delegate setupやImplicit setupが効果的なのはわかる
このへん、SWEの本にも「テストは繰り返し実行するから速度が大事」的なこと書いてあった気がする #要出典
柔軟性を持たせたり。複雑なテストフィクスチャならばIn-line setupで用意するしかないのでは?
重要なのは、テストフィクスチャを複雑でも柔軟に表現できる"表現力"があると便利!ってこと
じゃあ、どう複雑なものを柔軟に表現するの??
PHPでそこ考えるときに便利なのが vierge-noire/cakephp-fixture-factories ってわけ
これのおかげで、Laravelでは既に実現されてるModel FactoriesをCakePHPで使えるようになった🙌
具体的にはこんな感じに使えるよん
基本的に class UserFactory extends BaseFactory というかんじで読み込む
UserFactory::make() でModel Factoryがセットアップされる
この時点ではclass UserFactoryに定義されてる protected function setDefaultTemplate(): void の中で定義されているデータをセットアップするだけ
$userArray = UserFactory::make()->getEntity() で配列になったデータが出てくる
CakePHPだと UserFactory::make()->persist()とかってやると cakephp/orm の class Cake\ORM\Table で定義された class UsersTable extends Table でデータが保存されて、 class Cake\ORM\Entity で定義されてる class App\Model\Entity\User extends Entity のインスタンスがヒュッと手に入る!
そして UserFactory::make()->with('UserAddresses', UserAddressFactory::make(3)) みたいなかんじで簡単にassociationsも表現できる!!!
CakePHP3以上をサポートしてる
基本的にCakePHP3で導入された cakephp/orm のTableクラスとEntityクラスを前提に実装されてる
CakePHP2はがんばったら使えるかもだけど恩恵はあまり受けられないかな...
フレームワークの前提が namespaced じゃないので、どう読み込むかは課題だけどたぶん使えそうではあるけど公式にはサポートされてないし、そもそもEntityの恩恵は得られない
そしてCakePHPに関わらず使えるよん
vierge-noire/test-database-cleaner を使ってデータベースをcleanupする
cf. vierge-noire/test-database-cleaner: A package to clean dirty tables between each test in a PHPUnit test suite.
UserFactory::make()->getEntity()でデータが手に入るまでは一緒
データベースへ登録するには cakephp/orm に準拠したTableクラスとEntityクラスが必要になる
将来的には拡張ができるようになるかもしれないけど、現状は一通りEventCompilerとかを書かないと差し替えられないね...
ちなみにRubyだとFactoryBot全盛なのかな
他の言語だと??(時間が余れば)
おわりに(まとめ)
月並だがIn-line setupとDelegate setup / Implicit setupをバランスよく使って、メンテナブルでファストなテストスイートを運用してこうな!!!
お砂場
cf. PHPerKaigi2022でのプロポーザル案
自動テストにおけるテストフィクスチャ(Test fixture, 場合によってはテストコンテキストとも呼ぶ)は、数多の種類のデータを扱うであろう、ほぼ全てのソフトウェアにおいて必要になるテクニックです。
( #要出典 なのだが、 xUnit - Wikipedia の中で xUnit を構成する部分のひとつとしてテストフィクスチャに言及されていたので、まあよさそう。)
その中でもデータベースと密接に連携する Web アプリケーションにおいては、データベースにデータを投入し、そのデータをテストフィクスチャとして利用するテクニックは初期の頃から利用されてきました。
( #要出典 なのだが、Rails 2.x の段階ではすでにテストフィクスチャについて RailsGuides に言及されていたので、まあフルスタックフレームワークが登場した初期の頃から存在すると言ってよさそう。)
PHPにおいては、各種フルスタックフレームワークにテストフィクスチャを管理する機能が備わったりしていますが、一方で Fabricate という Ruby における factory_bot のような"柔軟な定義構文を用いてテストフィクスチャを管理する"ためのパッケージや、ここ最近になって CakePHP Fixture Factories という Fabricate に近しいコンセプトでテストフィクスチャを扱うパッケージが登場してきています。
本セッションでは、新たに登場した CakePHP Fixture Factories の解説を踏まえ、CakePHP ではない PHP プロジェクトでも CakePHP Fixture Factories が転用できる可能性を示しつつ、新たなテストフィクスチャのスタンダードについてお話できればと思います。
決定稿
自動テストにおけるテストフィクスチャは、ほぼ全てのソフトウェアにおいて必要になるテクニックです。その中でもデータベースと密接に連携する Web アプリケーションにおいては、データベースにデータを投入し、そのデータをフィクスチャとして利用するテクニックは初期の頃から利用されてきました。
PHPにおいては、 Fabricate という Ruby における factory_bot のような"柔軟な定義構文を用いてフィクスチャを管理する"ためのパッケージや、ここ最近になって CakePHP Fixture Factories という Fabricate に近しいコンセプトのパッケージが登場してきています。
本トークでは、最近登場した CakePHP Fixture Factories の解説を中心に、新たなテストフィクスチャ管理ツールのスタンダードについてお話できればと思います。
(393文字)
残骸
Static Fixtures
決まった値を流し込む
いつ?
テストスイートを一通り流す前とか
何がうれしい?
速い(テストケース実行時にはすでにオンメモリだし)
Dynamic Fixtures
動的に値を生成する
いつ?
テストケースを実行する前
何がうれしい?
柔軟(テストケースに合わせて準備するしね)
ストレージはStaticでもDynamicでも変わんない??
結局は値なので
直で値定義
ファイルに値
DBに保存
DBって便利なのよね
ID自動採番されるしシュンって取得できるしfilterもmapもreduceもSQLでできる
値の状態変化を追いつつテストする、みたいなこともしてそう
何でStatic/Dynamicを分けるのか
Hot/Coldでもよいかもめ
参考
xUnit - Wikipedia
Rubyのテスティングフレームワークの歴史(2014年版) - 2014-11-06 - ククログ
test-unit - Ruby用単体テストフレームワーク 
9. The 20th Anniversary of TDD by texta.fm
4.2. テストフィクスチャのしくみ | Rails テスティングガイド - Railsガイド
フィクスチャー | テスト - CakePHP 4.x Strawberry Cookbook
Defining Model Factories | Database Testing - Laravel - The PHP Framework For Web Artisans
Test fixture - Wikipedia
Shared Fixture at XUnitPatterns.com
vierge-noire/cakephp-fixture-factories: CakePHP Fixture Factories
vierge-noire/test-database-cleaner: A package to clean dirty tables between each test in a PHPUnit test suite.
#Techlog #PHP #phperkaigi #CakePHP #vierge-noire/cakephp-fixture-factories #テスト技法 #テストフィクスチャ